﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace IntoTheVoid.Units.Download
{
	public class DownloadController
	{
		private long fromIndex = 0;//开始下载的位置
		private bool isRun = false;//是否正在进行
		private DownloadInfo dlInfo;

		private List<DownloadService> downloadServiceList = new List<DownloadService>();

		public event Action<long> OnStart;
		public event Action OnStop;
		public event Action<long, double> OnDownload;
		public event Action OnFinsh;
		public event Action<double> OnCombineFile;
		public event Action OnCombineStartFile;
		public event Action<string> OnError;
		public int limitedSpeed = 100 * 1024 * 1024; // kb/s
		private Task task;
		public DownloadController(DownloadInfo dlInfo)
		{
			this.dlInfo = dlInfo;
		}
		public void Stop()
		{
			isRun = false;
			downloadServiceList.ForEach(dl => dl.Stop());
			OnStopHandler();
		}

		public void Start()
		{
			dlInfo.isReStart = false;
			WorkStart();
		}
		public void ReStart()
		{
			dlInfo.isReStart = true;
			WorkStart();
		}
		public void SetSpeed(int speed)
		{
			if (speed < 0)
			{
				speed = 100 * 1024 * 1024;
			}
			if (dlInfo.taskCount == 0)
				return;

			limitedSpeed = speed / dlInfo.taskCount;
			downloadServiceList.ForEach(dl => dl.SetSpeed(limitedSpeed));
		}
		private void WorkStart()
		{
			// if (task != null)
			// 	return;
			LogHelper.Info("WorkStart");
			task = Task.Run(() =>
			{
				if (dlInfo.isReStart)
				{
					Stop();
				}

				while (downloadServiceList.Where(dl => !dl.isStopped).Count() > 0)
				{
					if (dlInfo.isReStart)
						Thread.Sleep(100);
					else
						return;
				}

				isRun = true;

				//首次任务或者不支持断点续传的进入
				if (dlInfo.isNewTask || (!dlInfo.isNewTask && !dlInfo.IsSupportMultiThreading))
				{
					try
					{
						//第一次请求获取一小块数据，根据返回的情况判断是否支持断点续传
						using (var rsp = DownloadHttpHelper.Download(dlInfo.downloadUrlList[0], 0, 0, dlInfo.method))
						{
							var Disposition = rsp.Headers["Content-Disposition"];
							if (Disposition != null)
								dlInfo.fileName = Disposition.Split('=')[1];
							else
								dlInfo.fileName = System.IO.Path.GetFileName(rsp.ResponseUri.AbsolutePath);

							var length = DownloadHttpHelper.GetHttpLength(dlInfo.downloadUrlList[0]);

							//尝试获取 Content-Range 头部，不为空说明支持断点续传
							var contentRange = rsp.Headers["Content-Range"];
							if (contentRange != null)
							{
								LogHelper.Info("contentRange != null");

								dlInfo.count = long.Parse(contentRange.Split('/')[1]);
								if (length < 0)
									length = dlInfo.count;
								OnStartHandler(length);

								dlInfo.IsSupportMultiThreading = true;

								var tempFileName = Convert.ToBase64String(Encoding.UTF8.GetBytes(dlInfo.fileName)).ToUpper();
								tempFileName = tempFileName.Length > 32 ? tempFileName.Substring(0, 32) : tempFileName;
								dlInfo.tempFileName = tempFileName;
								GetTaskInfo(dlInfo);

							}
							else
							{
								// 不支持断点续传
								LogHelper.Info("contentRange == null");
								Save(GetRealFileName(dlInfo), rsp.GetResponseStream());
								OnFinishHandler();
							}
						}
					}
					catch (Exception ex)
					{
						LogHelper.Error(ex, "Download Error");
						OnErrorHandler(ex.Message);
					}
					dlInfo.isNewTask = false;
				}

				if (dlInfo.IsSupportMultiThreading)
				{
					StartTask(dlInfo);
					SetSpeed(limitedSpeed * dlInfo.taskCount);
					while (downloadServiceList.Where(td => !td.isFinish).Count() > 0 && isRun)
					{
						Thread.Sleep(100);
					}
					if ((downloadServiceList.Where(td => !td.isFinish).Count() == 0))
					{
						LogHelper.Info("td.isFinish");
						CombineFiles(dlInfo);
						OnFinishHandler();
					}
				}
				task = null;
			});

		}
		private void CombineFiles(DownloadInfo dlInfo)
		{
			LogHelper.Info("Enter CombineFiles");
			OnCombineFileStartHandler();

			string realFilePath = GetRealFileName(dlInfo);
			// 预计算总大小和倒数（避免重复计算和除法）
			long totalSize = dlInfo.TaskInfoList[dlInfo.TaskInfoList.Count - 1].toIndex;
			double inverseTotalSize = 100.0 / totalSize;

			// 使用更大的缓冲区（1MB）
			byte[] buffer = new byte[1024 * 1024];
			int length = 0;
			long totalLength = 0;
			int timePre = Environment.TickCount;

			try
			{
				// 预分配文件空间并优化FileStream选项
				using (var fileStream = new FileStream(realFilePath, FileMode.Create, FileAccess.Write, FileShare.None, buffer.Length, FileOptions.SequentialScan))
				{
					fileStream.SetLength(totalSize); // 预分配磁盘空间

					foreach (var taskInfo in dlInfo.TaskInfoList)
					{
						string tempFile = taskInfo.filePath;
						using (var tempStream = new FileStream(tempFile, FileMode.Open, FileAccess.Read, FileShare.Read, buffer.Length, FileOptions.SequentialScan))
						{
							while ((length = tempStream.Read(buffer, 0, buffer.Length)) > 0)
							{
								fileStream.Write(buffer, 0, length);
								totalLength += length;

								// 优化进度计算（乘法代替除法）
								int timeNow = Environment.TickCount;
								if (timeNow - timePre >= 1000)
								{
									OnCombineFileHandler(totalLength * inverseTotalSize);
									timePre = timeNow;
								}
							}
						}

						// 立即删除已合并的临时文件
						File.Delete(tempFile);
						LogHelper.Info($"Deleted temp file: {tempFile}");
					}
				}
				OnCombineFileHandler(100.0); // 确保最终进度到达100%
			}
			catch (Exception ex)
			{
				LogHelper.Error(ex, "CombineFiles Error");
				OnErrorHandler(ex.Message);
			}
		}

		private static string GetRealFileName(DownloadInfo dlInfo)
		{
			//创建正式文件名，如果已存在则加数字序号创建，避免覆盖
			var fileIndex = 0;
			var realFilePath = System.IO.Path.Combine(dlInfo.saveDir, dlInfo.fileName);
			while (File.Exists(realFilePath))
			{
				realFilePath = System.IO.Path.Combine(dlInfo.saveDir, string.Format("{0}_{1}", fileIndex++, dlInfo.fileName));
			}
			LogHelper.Info("FilePath" + realFilePath);
			return realFilePath;
		}

		private void StartTask(DownloadInfo dlInfo)
		{
			downloadServiceList = new List<DownloadService>();
			var limit = limitedSpeed / dlInfo.taskCount;
			if (dlInfo.TaskInfoList != null)
			{
				foreach (var item in dlInfo.TaskInfoList)
				{
					var downloadService = new DownloadService();
					downloadService.OnDownload += OnDownloadHandler;
					downloadService.OnError += OnErrorHandler;
					downloadServiceList.Add(downloadService);
					downloadService.Start(item, dlInfo.isReStart);
					downloadService.SetSpeed(limit);
				}
			}
		}

		private void GetTaskInfo(DownloadInfo dlInfo)
		{
			var pieceSize = (dlInfo.count) / dlInfo.taskCount;
			dlInfo.TaskInfoList = new List<TaskInfo>();
			var urlIndex = 0;
			for (int i = 0; i <= dlInfo.taskCount + 1; i++)
			{
				var from = (i * pieceSize);
				if (from >= dlInfo.count)
					break;

				var to = from + pieceSize;
				if (to >= dlInfo.count)
					to = dlInfo.count;

				dlInfo.TaskInfoList.Add(
					new TaskInfo
					{
						method = dlInfo.method,
						downloadUrl = dlInfo.downloadUrlList[urlIndex++],
						filePath = System.IO.Path.Combine(dlInfo.saveDir, dlInfo.tempFileName + i + ".temp"),
						fromIndex = from,
						toIndex = to
					});
				if (urlIndex >= dlInfo.downloadUrlList.Count)
					urlIndex = 0;
			}
		}

		private void Save(string filePath, Stream stream)
		{
			try	
			{
				using (var writer = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
				{
					using (stream)
					{
						var repeatTimes = 0;
						byte[] buffer = new byte[4024];
						var length = 0;
						while ((length = stream.Read(buffer, 0, buffer.Length)) > 0 && isRun)
						{
							writer.Write(buffer, 0, length);
							fromIndex += length;
							if (repeatTimes % 5 == 0)
							{
								writer.Flush();
								OnDownloadHandler();
							}
							repeatTimes++;
						}
						writer.Flush();
						OnDownloadHandler();
					}
				}
			}
			catch (Exception ex)
			{
				LogHelper.Error(ex, "Save File Error");
				OnErrorHandler(ex.Message);
			}
		}

		int timePev = 0;
		int timeNow = 0;
		long sizePre = 0;
		private void OnStartHandler(long totalSize)
		{
			timePev = Environment.TickCount;
			timeNow = Environment.TickCount;

			Task.Run(() =>
			{
				OnStart?.Invoke(totalSize);
			});
		}
		private void OnStopHandler()
		{
			Task.Run(() =>
			{
				Thread.Sleep(500);
				OnStop?.Invoke();
			});
		}
		private void OnFinishHandler()
		{
			Task.Run(() =>
			{
				for (int i = 0; i < dlInfo.TaskInfoList.Count; i++)
				{
					var tempFile = dlInfo.TaskInfoList[i].filePath;
					File.Delete(tempFile);
				}
				OnFinsh?.Invoke();
			});
		}
		private void OnDownloadHandler()
		{
			Task.Run(() =>
			{
				long current = GetDownloadLength();
				if (sizePre == 0)
					sizePre = current;	
				timeNow = Environment.TickCount;
				if (timeNow - timePev >= 1000 && current - sizePre > 0)
				{
					var speed = (double)(current - sizePre) * 1000 / (timeNow - timePev);
					sizePre = current;
					timePev = timeNow;
					OnDownload?.Invoke(current, speed);
				}

			});
		}
		private void OnCombineFileStartHandler()
		{
			OnCombineStartFile?.Invoke();
		}
		private void OnCombineFileHandler(double progress)
		{
			Task.Run(() =>
			{
				OnCombineFile?.Invoke(progress);
			});
		}
		private void OnErrorHandler(string message)
		{
			Task.Run(() =>
			{
				OnError?.Invoke(message);
			});
		}
		public long GetDownloadLength()
		{
			if (dlInfo.IsSupportMultiThreading)
				return downloadServiceList.Sum(dl => dl.GetDownloadedCount());
			else
				return fromIndex;
		}
	}

}
